package com.zkh.myframe.server.tomcat.websocket;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.apache.tomcat.websocket.AuthenticationException;

import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.json.JSONObject;
import com.zkh.myutils.json.JSONParseException;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.StringUtils;

@ServerEndpoint("/websocket/{part}")
public class WebSocketCore {
	//分支ID，此处为part参数值
	private String branchId;
	//会话连接
	private Session session;
	//用户唯一标识
	private String userKey;
	//分支列表
	private static List<String> branchList = new ArrayList<>();
	//自定义WebSocket
	private static Map<String, WebSocket> webSocketMap = new HashMap<>();
	
	/**
	 * 连接建立成功
	 * @param session 连接会话
	 */
	@OnOpen
	public void onOpen(Session session) {
		//获取分支参数
		branchId = session.getPathParameters().get("part");
		try {
			//分支地址
			int index = branchList.indexOf(branchId);
			//分支为空
			if(index==-1) {
				session.close(new CloseReason(CloseCodes.UNEXPECTED_CONDITION, "分支参数错误，分支不存在"));return;
			}
			//获取分支和分支处理对象
			String branch = branchList.get(index);
			WebSocket socket = webSocketMap.get(branch);
			//鉴权，获取用户唯一标识
			String userKey = socket.authentication(session);
			//保存会话
			this.session = session;
			this.userKey = userKey;
			//在线数量
			int onlineCount = WebSocketSessionUtils.addOnlineCount(branchId);
			//保存连接
			WebSocketSessionUtils.saveSocket(branchId, userKey, this);
			//日志
			Logger.info("有新连接加入！当前连接数：" + onlineCount + "【" + branchId + "】");
		}catch(AuthenticationException e) {
			//鉴权失败，关闭会话
			try{
				session.close(new CloseReason(CloseCodes.UNEXPECTED_CONDITION, e.getMessage()));
			}catch(IOException ee) {
				Logger.error("关闭会话时发生异常", ee);
			}
		}catch(IOException e) {
			Logger.error(e);
		}
	}
	
	/**
	 * 连接关闭
	 */
	@OnClose
	public void onClose() {
		//存在分支
		Assert.doIfNotEmpty(userKey, u->{
			//移除连接
			WebSocketSessionUtils.removeSocket(branchId, userKey, this);
			//在线数量
			int onlineCount = WebSocketSessionUtils.subtractOnlineCount(branchId);
			//日志
			Logger.info("有连接被关闭！当前连接数：" + onlineCount + "【" + branchId + "】");
		});
	}
	
	/**
	 * 接收到客户端消息
	 * @param message 接收到的消息
	 * @param session 会话
	 */
	@OnMessage
	public void onMessage(String message) {
		//不为空
		if(StringUtils.isNotEmpty(message)) {
			try {
				Logger.debug("接收到的消息：" + message);
				//解析参数
				WebSocketMessage msg = new JSONObject(message).parseEntity(WebSocketMessage.class);
				//组装内部信息
				msg.setSender(userKey);
				msg.setBranchId(branchId);
				//存在分支，调用对应的消息处理方法
				Assert.doIfNotNull(webSocketMap.get(branchId), webSocket->webSocket.receivedMessage(msg));
			}catch (JSONParseException e) {
				Logger.error("WebSocket消息参数解析异常", e);
			}
		}
	}
	
	/**
	 * 发生错误时调用
	 * @param session 会话
	 * @param error 异常对象
	 */
	@OnError
	public void onError(Session session, Throwable error) {
		//调用对应的错误处理方法
		Assert.doIfNotNull(webSocketMap.get(branchId), webSocket->webSocket.cacheError(session, error, userKey));
	}
	
	/**
	 * 发送消息
	 * @param message 发送的消息
	 * @throws IOException
	 */
	public void sendMessage(String message) throws IOException {
		Logger.debug("发送的消息：" + message);
		// 发送异步消息
		this.session.getAsyncRemote().sendText(message);
		// 发送同步消息
		//this.session.getBasicRemote().sendText("");
	}
	
	/**
	 * 添加分支，由框架自动调用
	 * @param branchId 分类名称
	 * @param webSocket 分支处理对象
	 */
	public static void addBranch(String branchId, WebSocket webSocket) {
		if(branchList.contains(branchId)) {
			throw new IllegalArgumentException("分支代码" + branchId + "重复");
		}
		//保存分支和WebSocket对象
		branchList.add(branchId);
		webSocketMap.put(branchId, webSocket);
	}
	
	/**
	 * 获取用户标识
	 * @return
	 */
	String getUserKey() {
		return userKey;
	}
	
	/**
	 * 关闭会话
	 */
	void close() {
		try {
			session.close(new CloseReason(CloseCodes.GOING_AWAY, "其它地方登录，被迫下线"));
		} catch (IOException e) {
			Logger.error(e);
		}
	}
}
